﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright(C) < 1998 - 2024 > Glodon Company Limited
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#if Gdmp || Gnuf
using Glodon.Gdmp.DB;
#elif Gap
using Glodon.Gap.DB;
using Glodon.Gap.UI;
#endif
using Glodon.Lookup.Core;
using Glodon.Lookup.Core.ModelBase;
using Glodon.Lookup.Models;
using Glodon.Lookup.Services.Enums;
using Glodon.Lookup.ViewModels.Contracts;
using Glodon.Lookup.ViewModels.Objects;

using System.Collections.ObjectModel;

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

using System.Windows.Data;
using System.ComponentModel;

using Glodon.Lookup.ViewModels.Utils;
using Glodon.Lookup.Utils;

namespace Glodon.Lookup.ViewModels.Pages.Tracebale
{
    public partial class SnoopHistoryViewModel : ObservableObject, ITraceableViewModel, ITabViewModel
    {
        private Comparer<object> _snoopableListComparer;

        public bool NeedReSelect;
        public SearchBoxMediator SearchBoxMediator { get; set; }

        public SnoopHistoryViewModel(SearchBoxMediator mediator)
        {
            SearchBoxMediator = mediator;
            _snoopableListComparer = Comparer<object>.Create(CompareSnoopableObjects);
            SnoopableViewSource = new CollectionViewSource();
            SnoopableViewSource.GroupDescriptions.Add(new PropertyGroupDescription("Descriptor.Type"));
            SnoopableViewSource.Filter += SnoopableViewSource_Filter;
            PropertyChanged += SnoopHistoryViewModel_PropertyChanged;
        }
        private void SnoopableViewSource_Filter(object sender, FilterEventArgs e)
        {
            e.Accepted = false;
            if (string.IsNullOrWhiteSpace(SearchBoxMediator.SearchText))
            {
                e.Accepted = true;
                return;
            }
            var snoopableObject = e.Item as SnoopableObject;
            if (snoopableObject!.Descriptor.Name.IndexOf(SearchBoxMediator.SearchText, StringComparison.OrdinalIgnoreCase) >= 0)
                e.Accepted = true;
            else if (snoopableObject.Descriptor.Type.IndexOf(SearchBoxMediator.SearchText, StringComparison.OrdinalIgnoreCase) >= 0)
                e.Accepted = true;
            else if (SearchEngine.MatchId(snoopableObject, SearchBoxMediator.SearchText))
            {
                e.Accepted = true;
            }
            if (snoopableObject is { IsSelected: true } && e.Accepted == false)
            {
                //因为对象即将被过滤，先取消选中
                snoopableObject.IsSelected = false;
                //将过滤前选中对象保存起来，如果过滤后结果为空，需要恢复过滤前的选中项，避免历史地址栏中当前选中信息丢失
                SnoopHistory.Last().CachedSelectedObject = snoopableObject;
                NeedReSelect = true;
            }
            return;
        }
        private void SnoopableObjects_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            SnoopableViewSource.View.Refresh();
        }
        public void TrySelectFirst(bool isInitial)
        {
            if (NeedReSelect || isInitial)
            {
                SnoopableObject? cachedSelectedObject = SnoopHistory.Last().CachedSelectedObject;
                if (SnoopableViewSource is { View: { } view } && view is CollectionView { Count: 0 })
                {
                    //过滤结果为空，且过滤前选中项还存在于当前Tab数据中（有可能因为视图改变或者文档改变等因素，之前选中的对象已经不存在于当前数据中）
                    //恢复过滤前的选中项，以避免历史地址栏中当前选中信息丢失
                    if (SnoopableObjects.Contains(cachedSelectedObject!))
                    {
                        if (cachedSelectedObject != null)
                        {
                            cachedSelectedObject.IsSelected = true;
                        }
                        SnoopableData = Array.Empty<Descriptor>();
                        NeedReSelect = false;
                        return;
                    }
                }
                if (SnoopableViewSource is { View: { Groups: { Count: > 0 } } collecView })
                {
                    //如果当前过滤结果有选中项，直接返回
                    IEnumerable<SnoopableObject> filtered = collecView.Cast<SnoopableObject>();
                    if (filtered.Any(s => s.IsSelected) || SnoopHistory is { Count: > 0 } && filtered.Contains(cachedSelectedObject))
                    {
                        if (cachedSelectedObject != null)
                        {
                            cachedSelectedObject.IsSelected = true;
                        }
                        return;
                    }

                    var firstGroup = SnoopableViewSource.View.Groups.First() as CollectionViewGroup;
                    if (firstGroup != null && firstGroup.Items.Any())
                    {
                        SnoopableObject? snoopableObject = firstGroup.Items.First() as SnoopableObject;
                        if (snoopableObject is not null)
                        {
                            snoopableObject.IsSelected = true;
                            NeedReSelect = false;
                        }
                    }
                }
            }
        }
        private int CompareSnoopableObjects(object x, object y)
        {
            try
            {
                if (x is not SnoopableObject xs || y is not SnoopableObject ys)
                {
                    throw new ArgumentException($"ListCollectionView自定义排序只支持{nameof(SnoopableObject)}");
                }
                //元素与自身比较结果应该为0
                if (IsCurrentTabObject(xs, Tab) && IsCurrentTabObject(ys, Tab))
                {
                    return 0;
                }
                //当前Tab的关联对象应该置顶
                if (IsCurrentTabObject(xs, Tab))
                {
                    return -1;
                }
                else if (IsCurrentTabObject(ys, Tab))
                {
                    return 1;
                }
                else
                {
                    //首先比较类型字符串
                    if (xs.Descriptor.Type.CompareTo(ys.Descriptor.Type) != 0)
                    {
                        return xs.Descriptor.Type.CompareTo(ys.Descriptor.Type);
                    }
                    //类型相同时再比较名字
                    if (xs.Descriptor.Name != null && xs.Descriptor.Name.CompareTo(ys.Descriptor.Name) != 0)
                    {
                        return xs.Descriptor.Name.CompareTo(ys.Descriptor.Name);
                    }
                    //名字相同时再比较描述
                    if (xs.Descriptor.Description != null && xs.Descriptor.Description.CompareTo(ys.Descriptor.Description) != 0)
                    {
                        return xs.Descriptor.Description.CompareTo(ys.Descriptor.Description);
                    }
                    //类型，名字，描述都相同时，如果是Element，就比较id
                    else if (xs.Object is Element xe && ys.Object is Element ye && xe.IsValidObject() && ye.IsValidObject())
                    {
                        return xe.Id.Int64Value.CompareTo(ye.Id.Int64Value);
                    }
                    else
                    {
                        if (xs.Descriptor.Description != null)
                        {
                            return -1;
                        }
                        if (ys.Descriptor.Description != null)
                        {
                            return 1;
                        }
                    }
                }
                return 0;
            }
            catch (Exception e)
            {
                ExceptionUtils.ShowExceptionMessage(e);
                return 0;
            }
        }

        private void UpdateViewSource()
        {
            SnoopableViewSource.Source = SnoopableObjects;
            //设置自定义排序比较器
            (SnoopableViewSource.View as ListCollectionView)!.CustomSort = _snoopableListComparer;

            //尝试选中排序后第一项
            if (SnoopHistory.Last().SelectedObject is null && SnoopHistory.Last().CachedSelectedObject is null)
            {
                var snoopableList = SnoopableObjects.ToList();
                snoopableList.Sort(_snoopableListComparer);
                var firstObj = snoopableList.FirstOrDefault();
                if (firstObj != null)
                {
                    firstObj.IsSelected = true;
                }
            }
        }
        private void SnoopHistoryViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals(nameof(SnoopableObjects)))
            {
                if (SnoopableObjects is { Count: > 0 })
                {
                    SnoopableObjects.CollectionChanged += SnoopableObjects_CollectionChanged;
                    UpdateViewSource();
                    TrySelectFirst(true);
                }
            };
        }

        public DataTabType Tab { get; set; }

        public ObservableCollection<SnoopAction> SnoopHistory { get; set; } = new ObservableCollection<SnoopAction>();

        public ObservableCollection<SnoopableObject> SnoopableObjects
        {
            get
            {
                if (SnoopHistory.Count > 0)
                {
                    return new ObservableCollection<SnoopableObject>(SnoopHistory.Last().Objects);
                }
                return null;
            }
        }

        public CollectionViewSource SnoopableViewSource { get; private set; }

        public IRelayCommand RefreshCommand => new RelayCommand(UpdateHome);

        [ObservableProperty]
        private IReadOnlyCollection<Descriptor> _snoopableData;

        [ObservableProperty]
        private int _historyLimit = 20;

        /// <summary>
        /// 判断指定可查看对象是否为当前Tab的关联对象（对于文档页，Document对象应为关联对象，对于视图页，应为ModelView对象）
        /// </summary>
        /// <param name="snoopableObject">指定的可查看对象</param>
        /// <param name="dataTab">当前Tab</param>
        /// <returns>为关联对象时返回 ture</returns>
        private bool IsCurrentTabObject(SnoopableObject snoopableObject, DataTabType dataTab)
        {
            bool isTabType = false;
            switch (dataTab)
            {
                case DataTabType.Document:
                    isTabType = snoopableObject.Object is Document;
                    break;
                case DataTabType.ModelView:
                    isTabType = snoopableObject.Object is ModelView;
                    break;
                default:
                    break;
            }
            return isTabType && (dataTab == DataTabType.Document || dataTab == DataTabType.ModelView);
        }

        public void AddHistory(IReadOnlyCollection<SnoopableObject> historyObjects)
        {

            if (SnoopHistory != null)
            {
                if (SnoopHistory.Count == HistoryLimit)
                {
                    //移除第二层历史（首页后的第一次Snoop）
                    SnoopHistory.RemoveAt(1);
                }
                SnoopAction action = new SnoopAction();
                action.Objects = historyObjects;
                SnoopHistory.Add(action);
                OnPropertyChanged(nameof(SnoopableObjects));
            }
        }

        [RelayCommand]
        private void BackForwardHistory()
        {
            if (SnoopHistory is { Count: > 1 })
            {
                SnoopHistory.RemoveAt(SnoopHistory.Count - 1);
                OnPropertyChanged(nameof(SnoopableObjects));
            }
        }

        public void ClearHistory()
        {
            SnoopHistory?.Clear();
        }

        [RelayCommand]
        private void ResetHistory(object parameter)
        {
            int from = -1;
            if (parameter is int)
            {
                from = (int)parameter;
                if (from < 0 || from >= SnoopHistory.Count)
                {
                    return;
                }
            }
            else if (parameter is SnoopAction item)
            {
                from = SnoopHistory.IndexOf(item) + 1;
            }
            if (from > -1 && SnoopHistory.Count > from)
            {
                int count = SnoopHistory.Count;
                //移除从指定位置起到最后的全部元素
                //（循环count-from次，每次都移除索引为from的元素）
                for (int i = from; i < count; i++)
                {
                    SnoopHistory.RemoveAt(from);
                }
                OnPropertyChanged(nameof(SnoopableObjects));
                //如果过滤后树中结果为空，右侧表格置空
                if (SnoopableViewSource is { View: { } view } && view is CollectionView { Count: 0 })
                {
                    SnoopableData = Array.Empty<Descriptor>();
                }

            }
        }

        /// <summary>
        /// 刷新绑定了最新历史数据的视图，供外部手动更新数据后调用
        /// </summary>
        public void RaiseSnoopableObjectsChanged()
        {
            OnPropertyChanged(nameof(SnoopableObjects));
        }
        /// <summary>
        /// 重新查询当前页数据，并对选中项进行恢复
        /// </summary>
        public void UpdateHome()
        {
            try
            {
                if (SnoopHistory is not { Count: > 0 })
                {
                    return;
                }
                IReadOnlyCollection<SnoopableObject> newObjects = Selector.Snoop(Tab);
                IReadOnlyCollection<SnoopableObject> objects = SnoopHistory.First().Objects;
                SnoopableObject? selectedOriginal = objects.Any(o => o.IsSelected) ? objects.First(o => o.IsSelected) : null;
                foreach (var newSnoopObj in newObjects)
                {
                    //SnoopableObject 更新了，但其内部对象 SnoopableObject.Object 可能是刷新前已有的实例，所以：

                    //如果当前 SnoopableObject 的内部对象与上次选中的 SnoopableObject 的内部对象相等，可以将当前SnoopableObject设为选中项
                    //（即：更新 SnoopableObject 集合时尽可能保持更新前的选中项）
                    if (SnoopHistory != null && SnoopHistory.Any())
                    {
                        if (selectedOriginal is { Object: { } } && newSnoopObj.Object.Equals(selectedOriginal.Object))
                        {
                            newSnoopObj.IsSelected = true;
                        }
                        else
                        {
                            //如果当前 SnoopableObject 的内部对象与上次选中的 SnoopableObject 的内部对象都是元素类型且具有相同元素ID，同上
                            if (newSnoopObj.Object is Element newElement && selectedOriginal is { Object: Element selElementOriginal })
                            {
                                ElementId originalSelId = null;
                                try
                                {
                                    originalSelId = selElementOriginal.Id;
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    bool valid = selElementOriginal.IsValidObject();
                                    if (valid && newElement.Id.Equals(selElementOriginal?.Id))
                                    {
                                        newSnoopObj.IsSelected = true;
                                    }
                                    //当旧元素无效，但其内部数据依然正常（sdk bug?）时，忽略无效状态
                                    else if (!valid && originalSelId != null && originalSelId.Equals(newElement.Id))
                                    {
                                        newSnoopObj.IsSelected = true;
                                    }
                                }
                                catch (Exception e)
                                {
                                    ExceptionUtils.ShowExceptionMessage(e);
                                }
                            }
                        }
                    }
                }
                if (SnoopHistory is { Count: > 0 })
                {
                    if (selectedOriginal is { Object: Element selElementOriginal } && !selElementOriginal.IsValidObject())
                    {
                        ResetHistoryCommand.Execute(1);
                    }
                    SnoopHistory.First().Objects = newObjects;
                    if (SnoopHistory.Count == 1)
                    {
                        OnPropertyChanged(nameof(SnoopableObjects));
                    }
                    SnoopHistory.First().RaiseSelectedObjectChanged();
                }
            }
            catch (Exception e)
            {
                ExceptionUtils.ShowExceptionMessage(e);
            }
        }

    }
}
